<template>
  <mxp-popup
    ref="popupRef"
    model="bottom"
    :animationEndShowContent="false"
    :showToolbar="options.showToolbar"
    :toolbarOptions="options.toolbarOptions"
    :background="options.background || defThemeColor.fill"
    :offsetBottom="options.offset"
    :zIndex="options.zIndex"
    :maskBackground="options.maskBackground"
    :maskClose="options.maskClose"
    @hide="data.showPicker = false"
    @animationEnd="onAnimationEnd"
  >
    <view style="width: 750rpx">
      <slot name="header"></slot>
      <view :style="{ height: `calc(${options.itemHeight} * ${options.showItemCount})` }">
        <view v-if="data.showPicker" class="picker-view-box-vessel">
          <view class="picker-view-box" :style="{ width: data.width }">
            <view v-if="options.range" class="subtitle-view">
              <text class="subtitle" :style="{ color: defThemeColor.text2 }">{{ options.subtitle?.[0] || '开始日期' }}</text>
            </view>
            <picker-view
              id="firstList"
              class="picker-view"
              :style="{ height: `calc(${options.itemHeight} * ${options.showItemCount})`, width: data.width }"
              :indicatorStyle="`height: ${options.itemHeight};line-height: ${options.itemHeight};`"
              :value="data.defaultDateIndex[0]"
              @change="bindChange"
              @pickstart="pickstart"
              @pickend="pickend"
            >
              <picker-view-column>
                <view v-for="(item, index) in data.firstList.yearArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                  <text
                    :style="{
                      fontSize: index === data.defaultDateIndex[0][2] ? options.activeFontSize : options.fontSize,
                      color: index === data.defaultDateIndex[0][2] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                    }"
                  >
                    {{ item }}年
                  </text>
                </view>
              </picker-view-column>
              <picker-view-column v-if="options.level! > 1">
                <view v-for="(item, index) in data.firstList.monthArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                  <text
                    :style="{
                      fontSize: index === data.defaultDateIndex[0][2] ? options.activeFontSize : options.fontSize,
                      color: index === data.defaultDateIndex[0][2] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                    }"
                  >
                    {{ item }}月
                  </text>
                </view>
              </picker-view-column>
              <picker-view-column v-if="options.level! > 2" key="picker-view-column-2">
                <view v-for="(item, index) in data.firstList.dayArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                  <text
                    :style="{
                      fontSize: index === data.defaultDateIndex[0][2] ? options.activeFontSize : options.fontSize,
                      color: index === data.defaultDateIndex[0][2] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                    }"
                  >
                    {{ item }}日
                  </text>
                </view>
              </picker-view-column>
            </picker-view>
          </view>
          <view v-if="options.range" class="picker-view-box" :style="{ width: data.width }">
            <view class="subtitle-view">
              <text class="subtitle" :style="{ color: defThemeColor.text2 }">{{ options.subtitle?.[1] || '结束日期' }}</text>
            </view>
            <picker-view
              id="secondList"
              class="picker-view"
              :style="{ width: data.width, minHeight: `200px` }"
              indicatorStyle="height: 40px;line-height: 40px;"
              :value="data.defaultDateIndex[1]"
              @change="bindChange"
              @pickstart="pickstart"
              @pickend="pickend"
            >
              <picker-view-column>
                <view v-for="(item, index) in data.secondList.yearArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                  <text
                    :style="{
                      fontSize: index === data.defaultDateIndex[1][0] ? options.activeFontSize : options.fontSize,
                      color: index === data.defaultDateIndex[1][0] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                    }"
                  >
                    {{ item }}年
                  </text>
                </view>
              </picker-view-column>
              <picker-view-column v-if="options.level! > 1">
                <view v-for="(item, index) in data.secondList.monthArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                  <text
                    :style="{
                      fontSize: index === data.defaultDateIndex[1][1] ? options.activeFontSize : options.fontSize,
                      color: index === data.defaultDateIndex[1][1] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                    }"
                  >
                    {{ item }}月
                  </text>
                </view>
              </picker-view-column>
              <picker-view-column v-if="options.level! > 2" key="picker-view-column-2">
                <view v-for="(item, index) in data.secondList.dayArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                  <text
                    :style="{
                      fontSize: index === data.defaultDateIndex[1][2] ? options.activeFontSize : options.fontSize,
                      color: index === data.defaultDateIndex[1][2] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                    }"
                  >
                    {{ item }}日
                  </text>
                </view>
              </picker-view-column>
            </picker-view>
          </view>
        </view>
      </view>
      <slot name="bottom"></slot>
    </view>
  </mxp-popup>
</template>

<script lang="ts" setup>
  import MxpPopup from '../mxp-popup/mxp-popup.vue';
  import type { $ToolbarProps } from '../mxp-toolbar/mxp-toolbar.vue';
  import { reactive, ref } from 'vue';
  import type { $AllColorStr, $SizeUnitStr } from '../mxp-tool/types/interface';
  import { removeObjectEmpty } from '../mxp-tool/utils/obj';
  import type { PickerViewOnChangeEvent } from 'mxp-uni-types';
  import { defThemeColor } from '../mxp-tool/theme/theme';

  export type $PickerDateProps = {
    /** 背景颜色 */
    background?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 文本的颜色 */
    color?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 选中的文本颜色 */
    activeColor?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 默认文字的大小 */
    fontSize?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 选中的默认文字的大小 */
    activeFontSize?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 偏移量 （如自定义底部的tabbar就需要一个偏移量） */
    offset?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 层级， 1代表仅选择年,2代表选择年和月, 3代表选择年月日 */
    level?: 1 | 2 | 3; // eslint-disable-line vue/no-unused-properties
    /** 是否为 范围选择 （开始和结束时间） */
    range?: boolean; // eslint-disable-line vue/no-unused-properties
    /** 最小日期 */
    minDate?: (Date | number | string)[] | Date | number | string; // eslint-disable-line vue/no-unused-properties
    /** 最大日期 */
    maxDate?: (Date | number | string)[] | Date | number | string; // eslint-disable-line vue/no-unused-properties
    /** 默认日期 */
    defaultDate?: (Date | number | string)[] | Date | number | string; // eslint-disable-line vue/no-unused-properties
    /** 是否留出安全区域 */
    safeSrea?: boolean; // eslint-disable-line vue/no-unused-properties
    /** zIndex */
    zIndex?: number; // eslint-disable-line vue/no-unused-properties
    /** 标题 */
    title?: string; // eslint-disable-line vue/no-unused-properties
    /** 标题 */
    subtitle?: [string, string]; // eslint-disable-line vue/no-unused-properties
    /** 是否显示顶部工具栏 */
    showToolbar?: boolean; // eslint-disable-line vue/no-unused-properties
    /** 顶部工具栏配置项 */
    toolbarOptions?: $ToolbarProps; // eslint-disable-line vue/no-unused-properties
    /** 每列显示多少排 */
    showItemCount?: number; // eslint-disable-line vue/no-unused-properties
    /** 单个选项的高度 */
    itemHeight?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 遮罩的背景色 */
    maskBackground?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 是否可以点击遮罩关闭 */
    maskClose?: boolean; // eslint-disable-line vue/no-unused-properties
  };
  export type $CallBack = {
    confirm?: (data: any) => void;
    change?: (data: { selected: any; type?: 'max' | 'min'; levelIndex: number; index: number; value: string }) => void;
  };
  export type $Options = $CallBack & $PickerDateProps;
  defineOptions({
    name: 'mxp-picker-date',
  });
  const props = withDefaults(defineProps<$PickerDateProps>(), {
    background: undefined as $AllColorStr | undefined,
    offset: '0px',
    level: 3,
    range: false,
    minDate: () => [new Date(new Date().getFullYear() - 100, 0, 1, 0, 0, 0), new Date(new Date().getFullYear() - 100, 0, 1, 0, 0, 0)],
    maxDate: () => [new Date(new Date().getFullYear() + 100, 11, 31, 23, 59, 59), new Date(new Date().getFullYear() + 100, 11, 31, 23, 59, 59)],
    defaultDate: () => [new Date(), new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 7)],
    safeSrea: true,
    zIndex: 888,
    title: '请选择日期',
    subtitle: () => ['开始日期', '结束日期'],
    toolbarOptions: () => ({}),
    showToolbar: true,
    color: undefined,
    activeColor: undefined,
    fontSize: '14px',
    activeFontSize: '16px',
    showItemCount: 7,
    itemHeight: '44px',
    maskBackground: undefined as $AllColorStr | undefined,
    maskClose: undefined as boolean | undefined,
  });
  defineSlots<{
    /** 自定义顶部内容 */
    header: () => void;
    /** 自定义底部内容 */
    bottom: () => void;
  }>();
  const global_date = new Date();
  const global_date_start = new Date(new Date().getFullYear() - 100, 0, 1, 0, 0, 0);
  const global_date_end = new Date(new Date().getFullYear() + 100, 11, 31, 23, 59, 59);

  const popupRef = ref<InstanceType<typeof MxpPopup> | null>(null);

  type $Data = {
    width: $SizeUnitStr;
    showPicker: boolean;
    isSubmit: boolean;
    firstList: { yearArr: string[]; monthArr: string[]; dayArr: string[] };
    secondList: { yearArr: string[]; monthArr: string[]; dayArr: string[] };
    defaultDateIndex: [number[], number[]];
    minDateNew: Date[];
    maxDateNew: Date[];
    defaultDateNew: Date[];
  };

  const options = ref<$CallBack & $PickerDateProps>(JSON.parse(JSON.stringify(props)));

  const data: $Data = reactive({
    width: '750rpx',
    showPicker: false,
    isSubmit: true,
    firstList: { yearArr: [], monthArr: [], dayArr: [] },
    secondList: { yearArr: [], monthArr: [], dayArr: [] },
    minDateNew: [],
    maxDateNew: [],
    defaultDateNew: [],
    defaultDateIndex: [
      [0, 0, 0],
      [0, 0, 0],
    ],
  });

  /** 显示 */
  function show(param: $Options = {}) {
    options.value = { ...props, ...(removeObjectEmpty(param) as $Options) };
    if (options.value.toolbarOptions) {
      options.value.toolbarOptions.title = options.value.title;
    }
    init();
    initData();
    popupRef.value?.show({
      confirm() {
        confirm();
      },
    });
  }
  /** 隐藏 */
  function hide() {
    popupRef.value?.hide();
  }

  /**
   * 更新日期 (外部调用此方法可更新日期)
   * @param date - 日期字符串或日期字符串数组
   */
  function undate(date: (Date | number)[] | Date | number) {
    initData(date);
  }

  /** 监听弹出框动画完成 */
  function onAnimationEnd(type: 'hide-animation' | 'show-animation') {
    if (type === 'show-animation') {
      data.showPicker = true;
    } else {
      data.showPicker = false;
    }
  }

  /** 提交 */
  function confirm() {
    if (data.isSubmit) {
      const arr: string[] = [];
      arr.push(getSubmitDateFun(data.defaultDateIndex[0], data.firstList, options.value.level || 3));
      if (options.value.range) {
        // true 返回数组 false返回字符串
        arr.push(getSubmitDateFun(data.defaultDateIndex[1], data.secondList, options.value.level || 3));
        if (arr[0]! > arr[1]!) {
          console.log('开始日期不能大于结束日期');
        } else {
          typeof options.value.confirm === 'function' && options.value.confirm(JSON.parse(JSON.stringify(arr)));
          hide();
        }
      } else {
        typeof options.value.confirm === 'function' && options.value.confirm(arr[0]!);
        hide();
      }
    }
  }

  /** 初始化 */
  function init() {
    data.showPicker = false;
    data.minDateNew = [];
    data.maxDateNew = [];
    data.defaultDateNew = [];
    data.firstList = { yearArr: [], monthArr: [], dayArr: [] };
    data.secondList = { yearArr: [], monthArr: [], dayArr: [] };
    data.defaultDateIndex = [
      [0, 0, 0],
      [0, 0, 0],
    ];
  }
  /** 初始化数据 */
  function initData(date = options.value.defaultDate) {
    data.width = options.value.range ? '350rpx' : '750rpx';
    const defaultDateNew = Array.isArray(date) ? [...date] : ([date] as (Date | number | string)[]);
    const minDateNew = Array.isArray(options.value.minDate) ? options.value.minDate : ([options.value.minDate] as (Date | number | string)[]);
    const maxDateNew = Array.isArray(options.value.maxDate) ? options.value.maxDate : ([options.value.maxDate] as (Date | number | string)[]);

    data.minDateNew[0] = getStringDate(minDateNew[0]!, 'start');
    data.maxDateNew[0] = getStringDate(maxDateNew[0]!, 'end');
    data.defaultDateNew[0] = getStringDate(defaultDateNew[0]!, 'default');
    trmAllDate(0);
    data.firstList = getDateArr(0, options.value.level!);
    data.defaultDateIndex[0] = getDateIndex(data.defaultDateNew[0], data.firstList.yearArr, data.firstList.monthArr, data.firstList.dayArr);
    if (options.value.range) {
      data.minDateNew[1] = getStringDate(minDateNew[1] || minDateNew[0] || '', 'start');
      data.maxDateNew[1] = getStringDate(maxDateNew[1] || maxDateNew[0] || '', 'end');
      data.defaultDateNew[1] = getStringDate(defaultDateNew[1] || defaultDateNew[0] || '', 'default');
      trmAllDate(1);
      data.secondList = getDateArr(1, options.value.level || 3);
      data.defaultDateIndex[1] = getDateIndex(data.defaultDateNew[1], data.firstList.yearArr, data.firstList.monthArr, data.firstList.dayArr);
    }
  }
  /** 滚轮改变 */
  function bindChange(e: PickerViewOnChangeEvent) {
    const val: number[] = e.detail.value;
    const id = e.target!.id as 'firstList' | 'secondList';
    const i = id === 'firstList' ? 0 : 1;
    const levelIndex = val.findIndex((item, idx) => item !== (data.defaultDateIndex[i][idx] || 0));
    const changeIndex = val[levelIndex]!;

    data.defaultDateIndex[i] = val;

    const y = parseInt(String(data[id].yearArr[val[0]!]));
    let m = parseInt(String(data[id].monthArr[val[1]!]));
    let d = parseInt(String(data[id].dayArr[val[2]!]));
    // 年份份改变，重置月和日
    if (levelIndex <= 0) {
      data[id].monthArr = getMonthArr(data.minDateNew[i]!, data.maxDateNew[i]!, [y, m, d]);
    }
    // 年份或者月份改变，重置日
    if (levelIndex <= 1) {
      m = parseInt(String(data[id].monthArr[val[1]!]));
      d = parseInt(String(data[id].dayArr[val[2]!]));
      data[id].dayArr = getDayArr(data.minDateNew[i]!, data.maxDateNew[i]!, [y, m, d]);
    }

    const keyObj = { 0: 'yearArr', 1: 'monthArr', 2: 'dayArr' };
    const key = keyObj[levelIndex as keyof typeof keyObj] as 'dayArr' | 'monthArr' | 'yearArr';
    const ksrq = getSubmitDateFun(data.defaultDateIndex[0], data.firstList, options.value.level || 3);
    if (options.value.range) {
      const type: 'max' | 'min' = id === 'firstList' ? 'min' : 'max';
      const jsrq = getSubmitDateFun(data.defaultDateIndex[1], data.secondList, options.value.level || 3);
      const res = { selected: [ksrq, jsrq], levelIndex, type, index: changeIndex, value: data[id][key][changeIndex]! };
      typeof options.value.change === 'function' && options.value.change(res);
    } else {
      const res = { selected: ksrq, levelIndex, index: changeIndex, value: data[id][key][changeIndex]! };
      typeof options.value.change === 'function' && options.value.change(res);
    }
  }

  /** 滚动开始 */
  function pickstart() {
    data.isSubmit = false;
  }

  /** 滚动结束 */
  function pickend() {
    data.isSubmit = true;
  }

  /** 得到年月日滚轮的数组 */
  function getDateArr(i: 0 | 1, level: 1 | 2 | 3) {
    const min = data.minDateNew[i]!;
    const max = data.maxDateNew[i]!;
    const def = data.defaultDateNew[i]!;
    const defArr = [def.getFullYear(), def.getMonth() + 1, def.getDate()];
    return {
      yearArr: createDateArray(min.getFullYear(), max.getFullYear()),
      monthArr: level > 1 ? getMonthArr(min, max, defArr) : [],
      dayArr: level > 2 ? getDayArr(min, max, defArr) : [],
    };
  }

  /** 得到月滚轮的数组 */
  function getMonthArr(min: Date, max: Date, defArr: number[]) {
    const minY = min.getFullYear();
    const maxY = max.getFullYear();
    const defY = defArr[0];
    const minM = min.getMonth() + 1;
    const maxM = max.getMonth() + 1;
    let minNumb = 1;
    let maxNumb = 12;
    if (minY === defY) {
      minNumb = minM;
    }
    if (maxY === defY) {
      maxNumb = maxM;
    }
    return createDateArray(minNumb, maxNumb);
  }

  /** 得到日滚轮的数组 */
  function getDayArr(min: Date, max: Date, defArr: number[]): string[] {
    const minY = min.getFullYear();
    const maxY = max.getFullYear();
    const defY = defArr[0]!;
    const minM = min.getMonth() + 1;
    const maxM = max.getMonth() + 1;
    const defM = defArr[1]!;
    const minD = min.getDate();
    const maxD = max.getDate();
    let minNumb = 1;
    if (minY === defY && minM === defM) {
      minNumb = minD;
    }

    let maxNumb = new Date(defY, defM, 0).getDate();
    if (maxY === defY && maxM === defM) {
      maxNumb = maxD;
    }
    return createDateArray(minNumb, maxNumb);
  }

  /** 创建数组 */
  function createDateArray(start: number, end: number): string[] {
    const arr = [];
    if (!isNaN(start) && !isNaN(end) && end - start <= 3000) {
      for (let i = start; i <= end; i++) {
        arr.push(getdoubleDigit(i));
      }
    }
    return arr;
  }

  /** 日期处理（容错）（遇到非法日期则转换成默认日期 */
  function getStringDate(dateStr: Date | number | string, type: 'default' | 'end' | 'start'): Date {
    let date = dateStr;
    if (typeof dateStr === 'string') {
      date = dateStr.substring(0, 19);
      date = date.replace(/-/g, '/'); // 必须把日期'-'转为'/'
    }
    date = new Date(date);
    if (isNaN(date.getFullYear()) || isNaN(date.getMonth()) || isNaN(date.getDate())) {
      date = type === 'start' ? global_date_start : type === 'end' ? global_date_end : global_date;
      if (dateStr) {
        console.error(`传入${type === 'start' ? '最小' : type === 'end' ? '最大' : '默认'}时间不合法。`);
      }
    }
    return date;
  }

  /**
   * 得到默认日期的下标
   */
  function getDateIndex(date: Date, yearArr: string[], monthArr: string[], dayArr: string[]) {
    const year = yearArr.indexOf(getdoubleDigit(date.getFullYear()));
    const month = monthArr.indexOf(getdoubleDigit(date.getMonth() + 1));
    const day = dayArr.indexOf(getdoubleDigit(date.getDate()));
    return [year > 0 ? year : 0, month > 0 ? month : 0, day > 0 ? day : 0];
  }

  /** 整理日期 */
  function trmAllDate(i: 0 | 1) {
    const min = data.minDateNew[i]!;
    const max = data.maxDateNew[i]!;
    const def = data.defaultDateNew[i]!;
    if (min.getTime() > max.getTime()) {
      data.minDateNew[i] = max;
      data.maxDateNew[i] = min;
      console.error(`传入最小日期大于最大日期，建议改正(本组件会自动以交换最大和最小日期)`);
    }
    if (data.minDateNew[i]!.getTime() > def.getTime()) {
      data.minDateNew[i] = def;
      console.error(`传入最小日期小于默认日期，建议改正(本组件会自动以自动把默认日期设为最小日期)`);
    }
    if (data.maxDateNew[i]!.getTime() < def.getTime()) {
      data.maxDateNew[i] = def;
      console.error(`传入最大日期小于默认日期，建议改正(本组件会自动以自动把默认日期设为最大日期)`);
    }
  }

  /**
   * 得到最终需要返回的时间
   */
  function getSubmitDateFun(indexArr: number[], dateList: { yearArr: string[]; monthArr: string[]; dayArr: string[] }, level: 1 | 2 | 3): string {
    const year = String(parseInt(dateList.yearArr[indexArr[0]!]!));
    let month: number | string = parseInt(dateList.monthArr[indexArr[1]!]!);
    let day: number | string = parseInt(dateList.dayArr[indexArr[2]!]!);
    month = month < 10 ? `0${month}` : month;
    day = day < 10 ? `0${day}` : day;
    const selectDate = level === 1 ? year : level === 2 ? `${year}-${month}` : `${year}-${month}-${day}`;
    return selectDate;
  }

  /** 得到两位数 */
  function getdoubleDigit(numb: number) {
    return numb < 10 ? `0${numb}` : `${numb}`;
  }

  defineExpose({
    /** 显示 */
    show,
    /** 隐藏 */
    hide,
    /** 更新日期 */
    undate,
    /** 提交 */
    confirm,
  });
</script>

<style lang="scss" scoped>
  .picker-view-box-vessel {
    display: flex;
    flex-flow: row nowrap;
    justify-content: center;
    width: 750rpx;

    .picker-view-box {
      padding-top: 10px;
      padding-bottom: 20px;

      .subtitle-view {
        display: flex;
        flex-flow: row nowrap;
        align-items: center;
        justify-content: center;
        margin-bottom: 10px;

        .subtitle {
          font-size: 13px;
        }
      }

      .picker-view {
        height: 200px;

        .picker-item {
          display: flex;
          flex-flow: column nowrap;
          align-items: center;
          justify-content: center;
          height: 40px;
          font-size: 13px;
          line-height: 40px;
          text-align: center;
        }
      }
    }
  }
</style>
